package de.unibi.comet.tools;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

import de.unibi.comet.fa.Alphabet;
import de.unibi.comet.fa.CDFA;
import de.unibi.comet.fa.DFAFactory;
import de.unibi.comet.fa.GeneralizedString;
import de.unibi.comet.fa.MarkovAdditiveChain;
import de.unibi.comet.fa.PrositeMotifParser;
import de.unibi.comet.util.Log;

public class motif_waiting_time {

	public static void usage() {
		System.out.println("usage: motif_stat <alphabet> <motif> <length>");
		System.out.println(" <motif>: one or more motifs (in prosite format), seperated by semicolons");
		System.exit(1);
	}

	public static void main(String[] args) {
		if (args.length!=3) usage();
		
		String motif = args[1];
		int steps = Integer.parseInt(args[2]);
		
		FileInputStream alphabetFile = null;
		try {
			alphabetFile = new FileInputStream(args[0]);
		} catch (FileNotFoundException e) {
			System.out.println("File not found, sorry!");
			System.exit(1);
		}
		BufferedReader br = new BufferedReader(new InputStreamReader(alphabetFile));
		ArrayList<String> l = new ArrayList<String>();
		HashMap<Character, Double> charMap = new HashMap<Character, Double>();
		try {
			while (true) {
				String line = br.readLine();
				if (line==null) break;
				StringTokenizer st = new StringTokenizer(line," ",false);
				if (!st.hasMoreTokens()) continue;
				char c = st.nextToken().charAt(0);
				if (!st.hasMoreTokens()) continue;
				double p = Double.parseDouble(st.nextToken());
				charMap.put(c, p);
				l.add(line);
//				System.out.printf("line: %s\n",line);
			}
		} catch (IOException e) {
			System.out.println("I/O failure, sorry!");
			System.exit(1);
		}

		Alphabet alphabet = new Alphabet(charMap.keySet());
		double[] charDist = new double[alphabet.size()];
		double sum = 0.0;
		for (int c=0; c<alphabet.size(); ++c) {
			charDist[c]=charMap.get(alphabet.get(c));
			sum+=charDist[c];
		}
		for (int c=0; c<alphabet.size(); ++c) charDist[c]/=sum;
		
		Log.getInstance().setTimingActive(true);
		Log.getInstance().setLogLevel(Log.Level.INSANE);
		//Log.getInstance().setLogLevel(Log.Level.EVERYTHING);

		StringTokenizer st = new StringTokenizer(motif,";",false);
		List<GeneralizedString> stringList = new ArrayList<GeneralizedString>();
		System.out.println(String.format("pattern:\"%s\"",motif));
		while (st.hasMoreTokens()) {
			String p = st.nextToken();
			try {
				stringList.addAll(PrositeMotifParser.parse(p));
				// System.out.println(l.size());
			} catch (IllegalArgumentException e) {
				System.out.println(String.format("invalid :\"%s\"",motif));
				return;
			}
		}
		Alphabet motifAlphabet = stringList.get(0).getAlphabet();
		if (!motifAlphabet.equals(alphabet)) {
			System.out.println("invalid alphabet");
			return;
		}
		
		// calculate occurrence probability
		double occProb = 0.0;
		for (GeneralizedString s : stringList) {
			occProb+=s.getProbability(charDist);
		}
		System.out.println(String.format("occurrence probability: %e / %e", occProb, 1.0-occProb));
		
		int nodeLimit = 1000;
		CDFA cdfa = DFAFactory.build(alphabet, stringList, nodeLimit);
//		double timeSubset = Log.getInstance().getLastPeriodCpu();
		int states = cdfa.getStateCount();
		System.out.println(String.format("dfa states: \"%d\"", states));
		
		cdfa = cdfa.minimizeHopcroft();
		int statesHopcroft = cdfa.getStateCount();
//		double timeHopcroft = Log.getInstance().getLastPeriodCpu();
		System.out.println(String.format("dfa states (after minimization): \"%d\"", statesHopcroft));
		
		MarkovAdditiveChain mac = cdfa.createMAC(charDist);
		// set maxmass = 0
		double[][] dist = new double[cdfa.getStateCount()][1];
		dist[0][0]=1.0;
		mac.setDistribution(dist);
		
		Log.getInstance().startTimer();
		mac.step(100);
		Log.getInstance().stopTimer("Markov additive chain stepping");
		double[] waitingTime = mac.waitingTime(cdfa.getOutputStates(), cdfa.getOutputStates(), steps);
		
//		Integer[] startStates = {0};
//	 	double[] waitingTime = mac.waitingTime(Arrays.asList(startStates), cdfa.getOutputStates(), steps);
		
		StringBuffer sb = new StringBuffer();
		sum = 0.0;
		for (int t=0; t<waitingTime.length; ++t) {
			sb.append(String.format("%e | ", waitingTime[t]));
			sum+=waitingTime[t];
		}
		sb.append(String.format("\nsum: %e", sum));
		System.out.println(sb.toString());
	}
}
